﻿using System;
using System.Collections.Generic;
using BobcatMultiTouch;
using CNShapes;
using BobcatUtility;
using CNPageViewer;
using CNNetworking;
using CNUtility;

namespace CNPageEditor
{
    /// <summary>
    /// Base class for all shape-editing (and related) actions.
    /// </summary><remarks>
    /// We use this base class to support the command-pattern. In addition
    /// to the standard undo() and redo() methods it also specifies other
    /// methods that are used for the shape-drawing application.
    /// 
    /// Receives touch events
    /// ---------------------
    /// In the drawing app, an action may not be a simple create-and-fire
    /// event. An action may take some time and responding to ongoing touch
    /// events while the action takes place. This includes drawing shapes, 
    /// dragging objects etc. So instead of creating an action and firing it, 
    /// we create an "active action" which receives and manages the touch events.
    /// 
    /// ActionCompletedEvent, ActionCanceledEvent
    /// -----------------------------------------
    /// As actions may take some time (see above) we need to be able to notify other 
    /// parts of application when the action is complete. We do this by firing the
    /// ActionCompletedEvent. We raise the ActionCanceledEvent if the action was canceled
    /// by the user.
    /// </remarks>
    public abstract class Action
    {
        #region Events

        /// <summary>
        /// Args passed to action-completed-events.
        /// </summary>
        public class Args : EventArgs
        {
        }

        /// <summary>
        /// Event raised when the action has completed.
        /// </summary>
        public event EventHandler<Args> ActionCompletedEvent;

        /// <summary>
        /// Derived classes can call this to raise the ActionCompletedEvent.
        /// </summary>
        protected void raiseActionCompletedEvent()
        {
            Utility.raiseEvent(ActionCompletedEvent, this, new Args());
        }

        /// <summary>
        /// Event raised if the action has been canceled.
        /// </summary>
        public event EventHandler<Args> ActionCanceledEvent;

        /// <summary>
        /// Derived classes can call this to raise the ActionCanceledEvent.
        /// </summary>
        protected void raiseActionCanceledEvent()
        {
            Utility.raiseEvent(ActionCanceledEvent, this, new Args());
        }

        #endregion

        #region Public methods

        /// <summary>
        /// Initializes the action with other objects it may need ("model" and "view").
        /// </summary>
        public void initialize(Page page, PageEditorControl pageEditorControl)
        {
            // We store the references to the model and view...
            m_Page = page;
            m_PageEditorControl = pageEditorControl;
            m_PageViewerControl = pageEditorControl.PageViewer;

            // We store the networking client ID of this instance of the app...
            m_ClientID = NetworkingClient.getInstance().ClientID;

            // We call initialize in the derived class...
            initialize();
        }

        #endregion

        #region Abstract methods

        /// <summary>
        /// Initializes a derived object. Called after the initialize in this base class.
        /// </summary>
        public abstract void initialize();

		/// <summary>
		/// Called just before the action is no longer the active action.
		/// This gives you a chance to unobserve any events that the action
		/// may have registered with.
		/// </summary>
		public abstract void deactivate();

        /// <summary>
        /// Returns a string description of the action that can be used (for example) as a tooltip in a toolbar.
        /// </summary>
        public abstract string getDescription();

        /// <summary>
        /// Returns true if this action can be undone, false otherwise.
        /// We generally want to support undo and redo for actions that affect the model
        /// (i.e. the picture) such as drawing, but not ones that affect the view such as
        /// moving or zooming. If this methods returns false, then the action will not
        /// be added to the undo stack.
        /// </summary>
        public abstract bool supportsUndo();

        /// <summary>
        /// Returns true if we should immediately create another one of these actions after
        /// the action has completed. Many drawing actions are repeatable, so that after drawing
        /// a box, for example, you go straight back into the box-drawing action so that you
        /// can draw another box. Some actions are not repeatable, for example showing the color
        /// picker dalog. We do not want to show another dialog as soon as you have chosen a color
        /// and closed it.
        /// </summary>
        /// <returns></returns>
        public abstract bool isRepeatable();

        /// <summary>
        /// Undoes the action (and stores the data necessary to redo it again later).
        /// </summary>
        public abstract void undo();

        /// <summary>
        /// Does the action again (after an undo).
        /// </summary>
        public abstract void redo();

        /// <summary>
        /// Called when a touch-down event occurs.
        /// </summary>
        public abstract void onTouchDown(TouchArgs touchArgs);

        /// <summary>
        /// Called when a touch-move event occurs.
        /// </summary>
        public abstract void onTouchMove(TouchArgs touchArgs);

        /// <summary>
        /// Called when a touch-up event occurs.
        /// </summary>
        public abstract void onTouchUp(TouchArgs touchArgs);

        #endregion

        #region Protected functions

        /// <summary>
        /// Returns an ActionOrigin indicating that an action originated
        /// from a user-action by the current client.
        /// </summary>
        protected ActionOrigin getActionOrigin()
        {
            return new ActionOrigin { OriginType = ActionOrigin.OriginTypeEnum.USER_ACTION, ClientID = m_ClientID };
        }

        #endregion

        #region Protected data

        protected Page m_Page = null; // The "model" we draw to.
        protected PageEditorControl m_PageEditorControl = null; // } These two classes make up the "view". They are 
        protected PageViewerControl m_PageViewerControl = null; // } useful to translate screen coords to shape coords, etc.
        protected Guid m_ClientID;

        #endregion

    }
}
